home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 May: Tool Chest / Developer CD Series May 1996 (Tool Chest) (Apple Computer) (1996).iso / Tool Chest / Development Tools & Languages / Dylan Related / Mindy / Mindy 1.2 - portable sources / libraries / random / distributions.dylan next >
Encoding:
Text File  |  1995-03-15  |  16.2 KB  |  522 lines  |  [TEXT/ttxt]

  1. module:     Random
  2. author:     dpierce@cs.cmu.edu
  3. synopsis:   This file implements random numbers for the Gwydion
  4.             implementation of Dylan.
  5. copyright:  Copyright (C) 1994, Carnegie Mellon University.
  6.             All rights reserved.
  7. rcs-header: $Header: distributions.dylan,v 1.3 94/12/06 11:20:44 wlott Exp $
  8.  
  9. //======================================================================
  10. //
  11. // Copyright (c) 1994  Carnegie Mellon University
  12. // All rights reserved.
  13. // 
  14. // Use and copying of this software and preparation of derivative
  15. // works based on this software are permitted, including commercial
  16. // use, provided that the following conditions are observed:
  17. // 
  18. // 1. This copyright notice must be retained in full on any copies
  19. //    and on appropriate parts of any derivative works.
  20. // 2. Documentation (paper or online) accompanying any system that
  21. //    incorporates this software, or any part of it, must acknowledge
  22. //    the contribution of the Gwydion Project at Carnegie Mellon
  23. //    University.
  24. // 
  25. // This software is made available "as is".  Neither the authors nor
  26. // Carnegie Mellon University make any warranty about the software,
  27. // its performance, or its conformity to any specification.
  28. // 
  29. // Bug reports, questions, comments, and suggestions should be sent by
  30. // E-mail to the Internet address "gwydion-bugs@cs.cmu.edu".
  31. //
  32. //======================================================================
  33.  
  34.  
  35. /* Random Number Distributions
  36.  
  37.    This file contains class definitions and functions for using random
  38.    number distributions.  The basis of these methods is the generation
  39.    of a uniform distribution on the interval [0, 1) using the Linear
  40.    Congruential Method.
  41.  
  42.    Other probability distributions are generated using transformations
  43.    of the basic unit uniform distribution.  Some that are defined here
  44.    are other uniform distribution, normal, and exponential
  45.    distributions.
  46.  
  47.    This implementation defines an abstract class
  48.    <random-distribution>.  All subclasses of <random-distribution>
  49.    must define a method for the generic function RANDOM.  This
  50.    function returns a random number in the distribution.
  51.  
  52. */
  53.  
  54. // <random-distribution> -- public
  55. //
  56. // Abstract superclass of random distributions.  Each subclass must
  57. // define a method for RANDOM, which returns a random number from the
  58. // distribution.
  59. //
  60. define abstract class <random-distribution> (<object>) end class;
  61.  
  62.  
  63. // random -- public
  64. //
  65. // Methods on this function return a random number generated from
  66. // whichever distribution is being used.
  67. //
  68. define generic random (random-distribution :: <random-distribution>)
  69.    => random :: <real>;
  70.  
  71.  
  72.  
  73. /* Unit Uniform Distribution
  74.  
  75.               Linear Congruential Method
  76.  
  77.    The basic algorithm used to generate random numbers is called the
  78.    Linear Congruential Method (see, for example, Sedgewick,
  79.    Algorithms, chapter 35).  The Linear Congruential method uses a
  80.    recurrence
  81.  
  82.                z  = a z    + 1 (mod m)
  83.                         k      k-1
  84.  
  85.    to generate pseudo-random sequences of integers uniformly
  86.    distributed between 0 and m - 1.  The first z is called the seed of
  87.    the sequence.
  88.  
  89.    There are some basic rules for the values of the constants (see
  90.    Sedgewick).  The value of m should be large.  Since Mindy has 30
  91.    bit integers, we take m to be
  92.  
  93.                                      28
  94.                 m = 2
  95.  
  96.                                   = 268 435 456
  97.  
  98.    The value of a should not be as large, perhaps one digit shorter
  99.    than m, and it should end with the three digits x21 where x is
  100.    even.  So we take a (arbitrarily) to be
  101.  
  102.                     a = 29 413 621
  103.  
  104.    Now, we cannot calculate each z directly because the calculation
  105.    would overflow the integer size.  Instead we multiply (a z) in
  106.    parts.  We take k to be the square root of m.  Then if
  107.  
  108.            p = p1 k + p0 and q = q1 k + q0
  109.  
  110.    then
  111.  
  112.          p q = (p1 q1) m + (p1 q0 + p0 q1) k + p0 q0
  113.  
  114.    and
  115.  
  116.       p q (mod m) = ((p1 q0 + p0 q1 (mod k)) k + p0 q0) (mod m)
  117.  
  118.    Using this method we can calculate (a z) (mod m) without overflow.
  119.  
  120.    Now a uniform distribution on [0, 1) can be found by dividing the
  121.    random variable z by m.
  122.  
  123. */
  124.  
  125. // <uniform-distribution> -- public
  126. // 
  127. // Abstract superclass of uniform distributions.  Uniform
  128. // distributions are distributions such that every element of the
  129. // range should appear with equal frequency.
  130. //
  131. define abstract class <uniform-distribution> (<random-distribution>) end class;
  132.  
  133.  
  134. // <unit-uniform-distribution> -- public
  135. //
  136. // This concrete class provides the implementation of the basic
  137. // uniform random number distribution on [0, 1).  It uses the linear
  138. // congruential method.
  139. //
  140. define class <unit-uniform-distribution> (<uniform-distribution>)
  141.    slot random-seed :: <integer>,
  142.       init-function: method () *dylan-random-seed* end method,
  143.       init-keyword: seed:;
  144. end class;
  145.  
  146.  
  147. // random -- public
  148. // 
  149. // Returns a random number from the unit uniform distribution.  Uses
  150. // the linear congruential method described above.  An integer between
  151. // 0 and m - 1 is generated.  This is put into the RANDOM-SEED slot of
  152. // the distribution and then divided by m to produce a real between 0
  153. // and 1.
  154. //
  155.  
  156. define constant $a$ = 29413621;
  157. define constant $m$ = ash(1, 28);
  158. define constant $k$ = ash(1, 14);
  159. define constant a1 = floor/ ($a$, $k$);
  160. define constant a0 = modulo ($a$, $k$);
  161.  
  162. define method random (dist :: <unit-uniform-distribution>)
  163.       => random :: <double-float>;
  164.    let z = dist.random-seed;
  165.    let z1 = floor/ (z, $k$);
  166.    let z0 = modulo (z, $k$);
  167.    let r = modulo (modulo (a1 * z0 + a0 * z1, $k$) * $k$ + a0 * z0 , $m$);
  168.    dist.random-seed := r;
  169.    as (<double-float>, r) / as (<double-float>, $m$)
  170. end method;
  171.  
  172.  
  173.  
  174. /* Other Uniform Distributions
  175.  
  176.    Most other random distribution generators rely on the unit uniform
  177.    generator.
  178.  
  179.    A uniform distribution of reals over an arbitrary interval [a, b)
  180.    can be obtained from the unit uniform random variable U by
  181.  
  182.               R = (b - a) U + a
  183.  
  184.    Similarly, a uniform distribution of integers over an arbitrary
  185.    interval [a, b] can be obtained from the unit uniform random
  186.    variable U by
  187.  
  188.               I = round ((b - a) U + a)
  189.  
  190. */
  191.  
  192. // <real-uniform-distribution> -- public
  193. //
  194. // The concrete class for real uniform distributions.  Slots for the
  195. // beginning and ending point of the distribution interval.
  196. //
  197. define class <real-uniform-distribution> (<uniform-distribution>)
  198.    slot unit-uniform :: <unit-uniform-distribution>;
  199.    slot random-from :: <real>,
  200.       required-init-keyword: from:;
  201.    slot random-to :: <real>,
  202.       required-init-keyword: to:;
  203. end class;
  204.  
  205.  
  206. // initialize -- interface
  207. //
  208. // The unit uniform distribution used in the uniform has to be set up.
  209. //
  210. define method initialize (dist :: <real-uniform-distribution>,
  211.               #key seed = *dylan-random-seed*)
  212.    dist.unit-uniform := make (<unit-uniform-distribution>, seed: seed);
  213. end method;
  214.  
  215.  
  216. // random -- public
  217. //
  218. // Generates real numbers which are distributed uniformly in some
  219. // interval.  Uses the unit uniform generator, and applies the linear
  220. // transformation described above.
  221. //
  222. define method random (dist :: <real-uniform-distribution>)
  223.       => random :: <double-float>;
  224.    (dist.random-to - dist.random-from) * random (dist.unit-uniform)
  225.       + dist.random-from
  226. end method;
  227.  
  228.  
  229. // <integer-uniform-distribution> -- public
  230. //
  231. // The concrete class for integer uniform distributions.  Slots for
  232. // the beginning and ending points of the distribution interval.
  233. //
  234. define class <integer-uniform-distribution> (<uniform-distribution>)
  235.    slot unit-uniform :: <unit-uniform-distribution>;
  236.    slot random-from :: <integer>,
  237.       required-init-keyword: from:;
  238.    slot random-to :: <integer>,
  239.       required-init-keyword: to:;
  240. end class;
  241.  
  242.  
  243. // initialize -- interface
  244. //
  245. // The unit uniform distribution used in the uniform has to be set up.
  246. //
  247. define method initialize (dist :: <integer-uniform-distribution>,
  248.               #key seed = *dylan-random-seed*)
  249.    dist.unit-uniform := make (<unit-uniform-distribution>, seed: seed);
  250. end method;
  251.  
  252.  
  253. // random -- public
  254. //
  255. // Generates integers which are distributed uniformly in some
  256. // interval.  Uses the unit uniform generator, and applies the linear
  257. // transformation described above.
  258. //
  259. // Note: The endpoints of the interval are both inclusive because of
  260. // the rounding.  That is, the numbers generate are in [a, b] instead
  261. // of [a, b).
  262. //
  263. define method random (dist :: <integer-uniform-distribution>)
  264.       => random :: <integer>;
  265.    round ((dist.random-to - dist.random-from) * random (dist.unit-uniform)
  266.          + dist.random-from)
  267. end method;
  268.  
  269.  
  270.  
  271. /* Exponential Distribution
  272.  
  273.    The exponential distribution has a cumulative distribution function
  274.    described by
  275.  
  276.                                    -lx
  277.                F(x) = 1 - e      x > 0
  278.  
  279.    An exponential distribution X can be generated from a unit uniform
  280.    distribution U using a transformation.  That is, a number u from
  281.    the uniform is chosen, then the inverse of the CDF is applied
  282.  
  283.                                     1
  284.                X(u) = - - ln(u)
  285.                                     l
  286.  
  287. */
  288.  
  289. // <exponential-distribution> -- public
  290. //
  291. // The concrete class for exponential distributions.  Slot for the
  292. // lambda parameter of the distribution.
  293. //
  294. define class <exponential-distribution> (<random-distribution>)
  295.    slot unit-uniform :: <unit-uniform-distribution>;
  296.    slot lambda :: <real>,
  297.       init-value: 1,
  298.       init-keyword: lambda:;
  299. end class;
  300.  
  301.  
  302. // initialize -- interface
  303. //
  304. // The unit uniform distribution used in the exponential has to be set
  305. // up.
  306. //
  307. define method initialize (dist :: <exponential-distribution>,
  308.               #key seed = *dylan-random-seed*)
  309.    dist.unit-uniform := make (<unit-uniform-distribution>, seed: seed);
  310. end method;
  311.  
  312.  
  313. // random -- public
  314. //
  315. // Generates numbers distributed in an exponential distribution with
  316. // parameter lambda.  Applies the inverse CDF of the exponential
  317. // distribution to a number generated from a unit uniform
  318. // distribution.
  319. //
  320. /* ### Needs log
  321. define method random (dist :: <exponential-distribution>)
  322.       => random :: <double-float>;
  323.    - (log (random (dist.unit-uniform)) / dist.lambda)
  324. end method;
  325. */
  326.  
  327.  
  328. /* Normal Distribution
  329.  
  330.    The normal (or Gaussian) probability distribution has a very
  331.    complicated probability density function.  So the methods used to
  332.    generate it are somewhat obscure.  The normal distribution can be
  333.    generated using two uniform distributions, A and B.  Numbers from a
  334.    normal distribution with mean 0 and standard deviation (or sigma) 1
  335.    can be found using this formula:
  336.  
  337.                                   1/2
  338.            X = (- 2 ln(A))    cos(2 pi B).
  339.  
  340.    This distribution can be transformed to a distribution with mean m
  341.    and sigma o by a linear function.
  342.  
  343.                  Y = o X + m
  344.  
  345. */
  346.  
  347. // <normal-distribution> -- public
  348. //
  349. // The concrete class for normal distributions.  Slots for the mean
  350. // and standard deviation (sigma) parameters of the distribution.
  351. //
  352. define class <normal-distribution> (<random-distribution>)
  353.    slot unit-uniform-A :: <unit-uniform-distribution>;
  354.    slot unit-uniform-B :: <unit-uniform-distribution>;
  355.    slot mean :: <real>,
  356.       init-value: 0,
  357.       init-keyword: mean:;
  358.    slot sigma :: <real>,
  359.       init-value: 1,
  360.       init-keyword: sigma:;
  361. end class;
  362.  
  363.  
  364. // initialize -- interface
  365. //
  366. // Both unit uniform distributions used in the normal have to be set
  367. // up.  The first is seeded from the seed given, and the second is
  368. // seeded from a random number generated by the first.
  369. //
  370. define method initialize (dist :: <normal-distribution>,
  371.               #key seed = *dylan-random-seed*)
  372.    dist.unit-uniform-A := make (<unit-uniform-distribution>, seed: seed);
  373.    dist.unit-uniform-B := make (<unit-uniform-distribution>,
  374.                 seed: random (dist.unit-uniform-A));
  375. end method;
  376.  
  377.  
  378. // random -- public
  379. // 
  380. // Generates a normal distribution with mean 0 and sigma 1 from two
  381. // numbers chosen from a unit uniform distribution, then applies the
  382. // linear transformation to adjust to the real mean and sigma of the
  383. // desired distribution.
  384. // 
  385. // (The constant $pi$ should be predefined or be defined as 4 *
  386. // atan(1) but we don't have atan.)
  387. //
  388. define constant $pi$ = 3.14159265358975;
  389. //
  390. /* ### Needs cos
  391. define method random (dist :: <normal-distribution>)
  392.       => random :: <double-float>;
  393.    let unit-normal-random = sqrt (-2 * log (random (dist.unit-uniform-A)))
  394.       * cos (2 * $pi$ * random (dist.unit-uniform-B));
  395.    dist.sigma * unit-normal-random + dist.mean
  396. end method;
  397. */
  398.  
  399.  
  400. /* Dylan Global Random Distribution
  401.  
  402.    In addition to the variety of random distributions this library
  403.    provides, we also want to have simpler functions for people to use
  404.    without having to create a distribution.
  405.  
  406.    Global variables hold a default Dylan random seed and distribution.
  407.    The function RANDOM-UNIFORM returns a number in some uniform
  408.    distribution.  The function SEED-RANDOM! allows the user to set the
  409.    global random seed.
  410.  
  411. */
  412.  
  413. // *dylan-random-seed* -- public
  414. // 
  415. // This global variable is used as the default seed for
  416. // <unit-uniform-distribution>s (and thus serves as the default seed
  417. // for most other random distributions.
  418. //
  419. define variable *dylan-random-seed* = 42424242;
  420.  
  421.  
  422. // *dylan-random-distribution* -- public
  423. // 
  424. // This global variable stores the default random distribution that is
  425. // used for the following functions.  This gives the user the
  426. // alternative of not having to create their own distributions.
  427. //
  428. define variable *dylan-random-distribution* =
  429.    make (<unit-uniform-distribution>, seed: *dylan-random-seed*);
  430.  
  431.  
  432. // random-uniform -- public
  433. // 
  434. // This function returns a random number from a uniform distribution.
  435. // The bounds of the uniform distribution are given by the keywords
  436. // from: and to:.  The type of the number returned is determined by
  437. // the type of the bounds.  (If the bounds do not have the same type
  438. // an error is signalled.)
  439. // 
  440. // If the bounds are <integer>, the random number is rounded.  If the
  441. // bounds are some other type (such as <single-float>, etc.), the
  442. // random number is coerced to that type.
  443. // 
  444. // This function uses *DYLAN-RANDOM-DISTRIBUTION* to generate the
  445. // random number.
  446. //
  447. define constant random-uniform =
  448.    method (#key from: from-bound = 0.0d0, to: to-bound = 1.0d0)
  449.       if (~ object-class (from-bound) == object-class (to-bound))
  450.      error ("Arguments to random-uniform must have same type.");
  451.       end if;
  452.       let random = (to-bound - from-bound)
  453.      * random (*dylan-random-distribution*)
  454.      + from-bound;
  455.       select (object-class (to-bound))
  456.      <integer> =>
  457.         round (random);
  458.      otherwise =>
  459.         as (object-class (to-bound), random);
  460.       end select;
  461.    end method;
  462.  
  463.  
  464. // seed-random! -- public
  465. //
  466. // This functions seeds the default Dylan distribution.
  467. //
  468. define constant seed-random! =
  469.    method (seed :: <integer>)
  470.       if (seed > 0)
  471.      *dylan-random-seed* := seed;
  472.      *dylan-random-distribution* :=
  473.         make (<unit-uniform-distribution>, seed: *dylan-random-seed*);
  474.       else
  475.      error ("Random seed must be > 0: %d", seed);
  476.       end if;
  477.       *dylan-random-seed*
  478.    end method;
  479.  
  480.  
  481.  
  482. /* Chi-Square Test for the Random Number Generator
  483.  
  484.    The chi-square function can be used to test the integrity of the
  485.    underlying linear congruential generator.  It takes a
  486.    <integer-uniform-distribution> (which should be a distribution on
  487.    an interval [0, r]) and calculates its chi square value.
  488.  
  489. */
  490.  
  491. // chi-square -- public
  492. // 
  493. // The chi square value of a integer uniform distribution on [0, r] is
  494. // found by taking the sum of the squares of the difference between
  495. // the frequencies of the elements of the distribution and the mean
  496. // frequency.  The mean frequency is the number of samples divided by
  497. // the number of elements in the interval (N / r).  For this to work,
  498. // N should be at least 10 r.
  499. // 
  500. // This function sets up a frequency array and generates N random
  501. // numbers and fills in their frequencies.  It then calculates the chi
  502. // square value of the distribution.
  503. // 
  504. // The chi square value should be no farther from r than twice the
  505. // square root of r.
  506. //
  507. define method chi-square (dist :: <integer-uniform-distribution>)
  508.    let r = dist.random-to;
  509.    let N = 10 * r;
  510.    let f = as (<double-float>, N) / as (<double-float>, r);
  511.    let freq = make (<vector>, size: r + 1, fill: 0);
  512.    for (i from 0 below N)
  513.       let d = random (dist);
  514.       freq[d] := freq[d] + 1;
  515.    end for;
  516.    for (i from 0 below r,
  517.     sample-sum = 0.0 then sample-sum + (freq[i] - f) ^ 2)
  518.    finally
  519.       sample-sum / f;
  520.    end for;
  521. end method;
  522.